<?php

namespace addons\yunfood\model;

use addons\yunfood\library\Logic;
use addons\yunfood\library\ModelTrait;
use addons\yunfood\library\Super;
use app\common\model\User;
use think\Db;
use think\Exception;
use think\exception\PDOException;
use think\Model;
use traits\model\SoftDelete;

/**
 * 订单模型
 */
class Order extends Model
{
    use ModelTrait;
    use SoftDelete;

    // 表名,不含前缀
    protected $name = 'yunfood_order';

    // 自动写入时间戳字段
    protected $autoWriteTimestamp = true;

    // 定义时间戳字段名
    protected $deleteTime = 'deleted_at';
    protected $updateTime = 'updated_at';
    protected $createTime = 'created_at';

    // 不显示的字段
    protected $hidden = ['deleted_at'];

    protected $resultSetType = 'collection';

    public function scopeShow($query)
    {
        return $query->where('status', '=', self::STATUS_WAIT_PAY);
    }

    // 商家数据
    public function shopData()
    {
        return $this->hasOne(Shop::class, 'id', 'shop_id');
    }

    // 桌号数据
    public function deskData()
    {
        return $this->hasOne(ShopDesk::class, 'id', 'desk_id');
    }

    // 用户数据
    public function userData()
    {
        return $this->hasOne(User::class, 'id', 'user_id');
    }

    // 订单商品列表数据
    public function orderItemListData()
    {
        return $this->hasMany(OrderItem::class, 'order_no', 'order_no');
    }

    public function getGoodsListAttr($value, $data)
    {
        $orderItems = $this->orderItemlistData;
        $onlyOneItem = count($orderItems) == 1;

        return Super::arrayGetSet($orderItems, [
            'order_item_no',
            'goods_id',
            'goods_name',
            'goods_names',
            'goods_type',
            'goods_type_text',
            'goods_cover' => function ($item) {
                return cdnurl($item['goods_cover'], true);
            },
            'goods_price',
            'goods_count',
            'balance',
            'payment',
            'payment_rel',
            'refund_no',
            'refund_status',
            'refund_status_text',
            'opt_actions' => function ($item) use ($onlyOneItem) {
                $optActions = self::getOrderItemOptActions($this, $item);
                if ($onlyOneItem) {
                    return array_diff($optActions, ['can_refund']);
                }
                return $optActions;
            }
        ], true);
    }

    // 订单类型
    const ORDER_TYPE_DEFAULT = 1;

    // 支付状态
    const PAYMENT_STATUS_WAIT   = 1;
    const PAYMENT_STATUS_DONE   = 2;
    const PAYMENT_STATUS_IGNORE = 3;

    public static function getPaymentStatusList()
    {
        return [
            self::PAYMENT_STATUS_WAIT   => __('未支付'),
            self::PAYMENT_STATUS_DONE   => __('已支付'),
            self::PAYMENT_STATUS_IGNORE => __('无需支付')
        ];
    }

    public function getPaymentStatusTextAttr($value, $data)
    {
        $optionList = self::getPaymentStatusList();

        return $optionList[$data['payment_status']] ?? $data['payment_status'];
    }

    public function getPaymentTimeTextAttr($value, $data)
    {
        if (empty($data['payment_time'])) {
            return '还未支付';
        }
        $splitTime = time() - $data['payment_time'];
        if ($splitTime < 300) {
            $timeStr = '5分钟内';
        } elseif ($splitTime < 900) {
            $timeStr = '15分钟内';
        } elseif ($splitTime < 1800) {
            $timeStr = '半小时内';
        } elseif ($splitTime < 3600) {
            $timeStr = '一小时内';
        } elseif ($splitTime < 5600) {
            $timeStr = '一个半小时内';
        } elseif ($splitTime < 5600) {
            $timeStr = '两小时内';
        } else {
            $timeStr = date('Y-m-d H:i', $data['payment_time']);
        }

        return $timeStr;
    }

    // 退款状态
    const REFUND_STATUS_NO     = 1;
    const REFUND_STATUS_ING    = 2;
    const REFUND_STATUS_DONE   = 3;
    const REFUND_STATUS_REFUSE = 4;
    const REFUND_STATUS_PART   = 5;
    const REFUND_STATUS_FAIL   = 6;

    public static function getRefundStatusList()
    {
        return [
            self::REFUND_STATUS_NO     => __('无'),
            self::REFUND_STATUS_ING    => __('退款中'),
            self::REFUND_STATUS_DONE   => __('退款完成'),
            self::REFUND_STATUS_REFUSE => __('已拒绝'),
            self::REFUND_STATUS_PART   => __('部分退款'),
            self::REFUND_STATUS_FAIL   => __('退款失败'),
        ];
    }

    public function getRefundStatusTextAttr($value, $data)
    {
        $optionList = self::getRefundStatusList();

        return $optionList[$data['refund_status']] ?? $data['refund_status'];
    }

    // 开票状态
    const INVOICE_STATUS_NO     = 1;
    const INVOICE_STATUS_ING    = 2;
    const INVOICE_STATUS_DOWN   = 3;
    const INVOICE_STATUS_REFUSE = 4;

    public static function getInoviceStatusList()
    {
        return [
            self::INVOICE_STATUS_NO     => __('未开票'),
            self::INVOICE_STATUS_ING    => __('开票中'),
            self::INVOICE_STATUS_DOWN   => __('开票完成'),
            self::INVOICE_STATUS_REFUSE => __('拒绝开票'),
        ];
    }

    public function getInvoiceStatusTextAttr($value, $data)
    {
        $optionList = self::getInoviceStatusList();

        return $optionList[$data['invoice_status']] ?? $data['invoice_status'];
    }

    // 打印状态
    const PRINT_STATUS_NO   = 1;
    const PRINT_STATUS_ING  = 2;
    const PRINT_STATUS_DOWN = 3;
    const PRINT_STATUS_FAIL = 4;

    public static function getPrintStatusList()
    {
        return [
            self::PRINT_STATUS_NO   => __('未打印'),
            self::PRINT_STATUS_ING  => __('打印中'),
            self::PRINT_STATUS_DOWN => __('打印完成'),
            self::PRINT_STATUS_FAIL => __('打印失败'),
        ];
    }

    public function getPrintStatusTextAttr($value, $data)
    {
        $optionList = self::getPrintStatusList();

        return $optionList[$data['print_status']] ?? $data['print_status'];
    }

    // 就餐方式
    const ENJOY_TYPE_MEAT = 'meat';
    const ENJOY_TYPE_PACK = 'pack';

    public static function getEnjoyTypeList()
    {
        return [
            self::ENJOY_TYPE_MEAT => __('店内用餐'),
            self::ENJOY_TYPE_PACK => __('打包带走'),
        ];
    }

    public function getEnjoyTypeTextAttr($value, $data)
    {
        $optionList = self::getEnjoyTypeList();

        return $optionList[$data['enjoy_type']] ?? $data['enjoy_type'];
    }

    // 状态
    const STATUS_WAIT_PAY    = 1;
    const STATUS_HAS_FINISH  = 2;
    const STATUS_HAS_CANCEL  = 3;
    const STATUS_HAS_TIMEOUT = 4;
    const STATUS_HAS_CLOSE   = 5;

    public static function getStatusList()
    {
        return [
            self::STATUS_WAIT_PAY    => __('待支付'),
            self::STATUS_HAS_FINISH  => __('已完成'),
            self::STATUS_HAS_CANCEL  => __('已取消'),
            self::STATUS_HAS_TIMEOUT => __('已失效'),
            self::STATUS_HAS_CLOSE   => __('已关闭')
        ];
    }

    public function getStatusTextAttr($value, $data)
    {
        $optionList = self::getStatusList();

        return $optionList[$data['status']] ?? $data['status'];
    }

    public static function getOrderOptActions($order, $retry = false)
    {
        $actions = [];
        if ($order->status == self::STATUS_WAIT_PAY) {
            $actions[] = 'can_cancel';
            $actions[] = 'can_pay';
        } else {
            if ($order->payment_status == self::PAYMENT_STATUS_DONE) {
                // 退款和开票设置
                $baseConfig = Config::getGroupAttributes(Config::GROUP_BASE);
                $orderRefundSwitch = $baseConfig['order_refund'] ?? 0;
                $orderInvoiceSwitch = $baseConfig['order_invoice'] ?? 0;

                if ($order->refund_status == self::REFUND_STATUS_NO) {
                    if ($orderRefundSwitch
                        && in_array($order->invoice_status, [self::INVOICE_STATUS_NO, self::INVOICE_STATUS_REFUSE])) {
                        $actions[] = 'can_refund';
                    }
                } elseif ($order->refund_status != self::REFUND_STATUS_PART) {
                    if ($order->refund_no) {
                        $actions[] = 'refund_result';
                    }
                    // 已拒绝、已失败的退款申请可以重新发起退款【暂不开启，避免用户重复提交】
//                    if (in_array($order->refund_status, [self::REFUND_STATUS_REFUSE, self::REFUND_STATUS_FAIL])
//                        && $order->refund_no && $retry) {
//                        $orderRefund = OrderRefund::get(['refund_no' => $order->refund_no]);
//                        if (! $orderRefund
//                            || in_array($orderRefund->status, [OrderRefund::STATUS_REFUSE, OrderRefund::STATUS_FAIL])) {
//                            $actions[] = 'can_refund';
//                        }
//                    }
                }

                if ($order->invoice_status == self::INVOICE_STATUS_NO) {
                    if ($orderInvoiceSwitch
                        && in_array($order->refund_status,
                            [self::REFUND_STATUS_NO, self::REFUND_STATUS_REFUSE, self::REFUND_STATUS_FAIL])) {
                        $actions[] = 'can_invoice';
                    }
                } else {
                    $actions[] = 'invoice_result';
                    // 已拒绝的开票申请可以重新发起开票【暂不开启，避免用户重复提交】
//                    if ($order->invoice_status == self::INVOICE_STATUS_REFUSE
//                        && $order->invoice_no && $retry) {
//                        $orderInvoice = OrderInvoice::get(['invoice_no' => $order->invoice_no]);
//                        if (! $orderInvoice || $orderInvoice->status == OrderInvoice::STATUS_REFUSE) {
//                            $actions[] = 'can_invoice';
//                        }
//                    }
                }
            }
        }

        return $actions;
    }

    public static function getOrderItemOptActions($order, $orderItem)
    {
        $actions = [];

        // 支付完成
        if ($order->payment_status == self::PAYMENT_STATUS_DONE) {
            // 退款设置
            $baseConfig = Config::getGroupAttributes(Config::GROUP_BASE);
            $orderRefundSwitch = $baseConfig['order_refund'] ?? 0;

            // 子订单未退款
            if ($orderItem->refund_status == OrderItem::REFUND_STATUS_NO) {
                if ($orderRefundSwitch) {
                    // 订单未退款或者部分退款
                    if (in_array($order->refund_status, [self::REFUND_STATUS_NO, self::REFUND_STATUS_PART])) {
                        // 订单未开票
                        if (in_array($order->invoice_status, [self::INVOICE_STATUS_NO, self::INVOICE_STATUS_REFUSE])) {
                            $actions[] = 'can_refund';
                        }
                    }
                }
            } else {
                $actions[] = 'refund_result';
                // 已拒绝、已失败的退款申请可以重新发起退款【暂不开启，避免用户重复提交】
//                if (in_array($orderItem->refund_status,
//                        [OrderItem::REFUND_STATUS_REFUSE, OrderItem::REFUND_STATUS_FAIL])
//                    && $orderItem->refund_no && $retry) {
//                    $orderRefund = OrderRefund::get(['refund_no' => $orderItem->refund_no]);
//                    if (! $orderRefund
//                        || in_array($orderRefund->status, [OrderRefund::STATUS_REFUSE, OrderRefund::STATUS_FAIL])) {
//                        $actions[] = 'can_refund';
//                    }
//                }
            }
        }

        return $actions;
    }

    /**
     * 完成订单
     *
     * @param $orderNo
     * @param int $paymentRel
     * @param string $paymentName
     * @param int $paymentStatus
     */
    public static function finishOrder(
        $orderNo,
        $paymentRel = 0,
        $paymentName = '',
        $paymentStatus = self::PAYMENT_STATUS_DONE
    ) {
        // 开启事务
        Db::startTrans();
        try {
            if (! $order = self::where('order_no', $orderNo)->lock(true)->find()) {
                throw new Exception('订单不存在');
            }
            if ($paymentRel) {
                $order->payment_rel = $paymentRel;
            }
            $order->payment_status = $paymentStatus;
            $order->payment_time = time();
            $order->payment_name = $paymentName;
            $order->status = self::STATUS_HAS_FINISH;
            $order->print_status = self::PRINT_STATUS_ING;
            $order->save();

            $orderItems = OrderItem::where('order_no', $order->order_no)->lock(true)->select();
            foreach ($orderItems as $item) {
                if ($paymentRel) {
                    $item->payment_rel = $item->payment - $item->balance;
                    $item->save();
                }
                // 菜品
                Food::where('id', $item->goods_id)->inc('count_sale', $item->goods_count)->update();
                if ($goodsData = json_decode($item->goods_data, true)) {
                    // 规格
                    if ($specData = $goodsData['spec_data'] ?? null) {
                        FoodSku::where('id', $specData['id'])->inc('count_sale', $item->goods_count)->update();
                    }
                    // 夹菜
                    if ($addonList = $goodsData['addon_list'] ?? null) {
                        $addonIds = array_column($addonList, 'id');
                        Food::where('id', 'in', $addonIds)->inc('count_sale')->update();
                    }
                }
            }
            // 统计
            if ($order->desk_id) {
                ShopDesk::where('id', $order->desk_id)
                    ->inc('order_count')
                    ->inc('cost_amount', $order->payment)->update([
                        'last_order_no'     => $order->order_no,
                        'last_order_time'   => time(),
                        'last_order_status' => 0
                    ]);
            }
            Db::commit();
        } catch (PDOException | Exception $e) {
            Db::rollback();
            throw $e;
        }

        // 实时打印
        if ($order = self::get(['order_no' => $orderNo])) {
            try {
                Logic::startPrint($order, PrinterDevice::SCENE_ORDER);
                $order->print_times++;
                $order->print_status = self::PRINT_STATUS_DOWN;
            } catch (Exception $e) {
                $order->print_status = self::PRINT_STATUS_FAIL;
            }
            $order->print_time = time();
            $order->save();
            ShopDesk::update([
                'last_order_status' => $order->print_status == self::PRINT_STATUS_DOWN ? 1 : 0
            ], ['id' => $order->desk_id]);
        }
    }

    /**
     * 取消订单
     *
     * @param $orderNo
     * @param int $cancelStatus
     */
    public static function cancelOrder($orderNo, $cancelStatus = self::STATUS_HAS_CANCEL, $changeOrder = true)
    {
        // 开启事务
        Db::startTrans();
        try {
            if (! $order = self::where('order_no', $orderNo)->lock(true)->find()) {
                throw new Exception('订单不存在');
            }
            if ($changeOrder) {
                if ($order->status != Order::STATUS_WAIT_PAY) {
                    throw new Exception('订单无效');
                }

                // 更新订单
                $order->status = $cancelStatus;
                $order->save();
            }

            // 退库存
            $orderItemList = OrderItem::where(['order_no' => $order->order_no])->select();
            foreach ($orderItemList as $orderItem) {
                if ($goodsData = json_decode($orderItem->goods_data, true)) {
                    // 规格
                    if ($specData = $goodsData['spec_data'] ?? null) {
                        FoodSku::where('id', $specData['id'])->inc('count_left', $orderItem->goods_count)->update();
                    }
                    // 加餐
                    if ($addonList = $goodsData['addon_list'] ?? null) {
                        $addonIds = array_column($addonList, 'id');
                        Food::where('id', 'in', $addonIds)->inc('count_left')->update();
                    }
                }
                // 菜品
                Food::where('id', $orderItem->goods_id)->inc('count_left', $orderItem->goods_count)->update();
            }

            // 余额退回
            if ($order->balance > 0) {
                FundUser::amountCounter($order->user_id, FundRecord::ACTION_REFUND,
                    $order->balance, FundRecord::SOURCE_TYPE_ORDER, $order->order_no);
            }
            Db::commit();
        } catch (PDOException | Exception $e) {
            Db::rollback();
            throw $e;
        }
    }
}
